package com.kly.manager;

import cn.hutool.core.util.StrUtil;
import com.kly.constants.RedisConstant;
import com.kly.exception.BusinessException;
import com.kly.exception.ErrorCode;
import com.kly.exception.ThrowUtils;
import com.kly.manager.auth.StpKit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.Instant;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

import static com.kly.constants.RedisConstant.INCR_AND_GET;

/**
 * @author admin
 * @date 2025/5/5
 */
@Slf4j
@Component
public class CounterManager {

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 获取计数 默认60秒超时
     *
     * @param key
     * @return
     */
    public long incrAndGet(String key) {
        return incrAndGet(key, 60, TimeUnit.SECONDS, 60);
    }


    /**
     * 获取计数
     *
     * @param key
     * @param interval
     * @param unit
     * @param timeout
     * @return
     */
    public long incrAndGet(String key, long interval, TimeUnit unit, long timeout) {
        //     判断key
        if (StrUtil.isBlank(key)) {
            return 0;
        }
        //     生成对应时间区间
        // 根据时间粒度生成 redisKey
        long timeFactor;
        switch (unit) {
            case SECONDS:
                timeFactor = Instant.now().getEpochSecond() / interval;
                break;
            case MINUTES:
                timeFactor = Instant.now().getEpochSecond() / 60 / interval;
                break;
            case HOURS:
                timeFactor = Instant.now().getEpochSecond() / 3600 / interval;
                break;
            default:
                throw new IllegalArgumentException("Unsupported TimeUnit. Use SECONDS, MINUTES, or HOURS.");
        }
        //     调用lua 脚本
        String redisKey = key + ":" + timeFactor;
        RedisScript<Long> redisScript = new DefaultRedisScript<>(INCR_AND_GET, Long.class);
        Long res = stringRedisTemplate.execute(
                redisScript,
                Collections.singletonList(redisKey),
                String.valueOf(timeout)
        );
        ThrowUtils.throwIf(res == null, ErrorCode.OPERATION_ERROR, "redis执行异常");
        return res;
    }


    /**
     * 检测爬虫
     */
    public void checkSpider() {
        int WARN_SIZE = 10;
        int BAN_SIZE = 20;

        //     获取用户id
        long userId = StpKit.SPACE.getLoginIdAsLong();

        String key = String.format("%s%s", RedisConstant.SPIDER_KEY, userId);

        long count = incrAndGet(key);
        if (count > BAN_SIZE) {
            // 强制下线
            StpKit.SPACE.kickout(userId);
            // 禁用10分钟
            StpKit.SPACE.disable(userId, 10 * 60);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "爬虫检测");
        }

        ThrowUtils.throwIf(count == WARN_SIZE, ErrorCode.OPERATION_ERROR, "警告，不要频繁访问");
    }
}
